#include "main_memory.h"

#include <QDebug>

MainMemory::MainMemory(Node *parent, int cas, int multiplexer, int size) :
    Node(parent),
    request_receiver(new ChannelLoadReqFabricReceiver(this)),
    load_sender(new ChannelLoadFabricSender(this)),
    store_receiver(new ChannelStoreFabricReceiver(this)),
    reg_data(QVector<std::complex<float>>(size))
{
    this->cas = cas;
    this->multiplexer = multiplexer;
}

void MainMemory::preload(const QVector<AllocOp> &aops)
{
    for(auto aop : aops)
    {
        //Set the data.
        reg_data[aop.addr] = aop.data;
    }
    printf("Preload complete.");
}

NodeTaskList MainMemory::tasks() const
{
    NodeTaskList list;
    list.append(static_cast<NodeTask>(&MainMemory::task_check_read));
    list.append(static_cast<NodeTask>(&MainMemory::task_check_write));
    return list;
}

void MainMemory::task_check_read()
{
    if(!queue_read.isEmpty() && !load_sender->Bus->is_any_full())
    {
        for(int i=queue_read.size()-1; i>-1; --i)
        {
            if(queue_read.at(i).cas_count <= 0)
            {
                load_sender->Bus->put(queue_read.at(i).data,
                                      queue_read.at(i).addr,
                                      queue_read.at(i).pid,
                                      queue_read.at(i).sb_addr,
                                      queue_read.at(i).ain_addr,
                                      queue_read.at(i).clr);
                queue_read.removeAt(i);
            }
            else
            {
                --queue_read[i].cas_count;
            }
        }
    }
    for(int m=0; m<multiplexer; ++m)
    {
        // Check request.
        if(!request_receiver->Bus->is_any_empty())
        {
            auto temp = request_receiver->Bus->get();
            ReadReq req;
            if(temp.mem_addr > -1 && temp.mem_addr < reg_data.size())
            {
                req.data = reg_data.at(temp.mem_addr);
            }
            req.addr = temp.sm_addr;
            req.pid = temp.pid;
            req.sb_addr = temp.sb_addr;
            req.ain_addr = temp.ain_addr;
            req.clr = temp.clr;
            req.cas_count = cas;
            queue_read.append(req);
        }
    }
}

void MainMemory::task_check_write()
{
    if(!queue_write.isEmpty() && !load_sender->Bus->is_any_full())
    {
        for(int i=queue_write.size()-1; i>-1; --i)
        {
            if(queue_write.at(i).cas_count <= 0)
            {
                if(queue_write.at(i).addr < 0 || queue_write.at(i).addr > reg_data.size())
                {
                    throw std::exception("Invalid write memory address access.\n");
                }
                reg_data[queue_write.at(i).addr] = queue_write.at(i).data;
                queue_write.removeAt(i);
            }
            else
            {
                --queue_write[i].cas_count;
            }
        }
    }
    for(int m=0; m<multiplexer; ++m)
    {
        if(!store_receiver->Bus->is_any_empty())
        {
            auto temp = store_receiver->Bus->get();
            WriteReq req;
            req.data = temp.data;
            req.addr = temp.addr;
            req.cas_count = cas;
            queue_write.append(req);
        }
    }
}
